<!DOCTYPE HTML>
<!--
	Dimension by HTML5 UP
	html5up.net | @ajlkn
	Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
-->
<html>
 <head>
  <title>
   Dimension by HTML5 UP
  </title>
  <!-- <meta charset="utf-8" /> -->
  <!-- <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" /> -->
  <meta charset="utf-8"/>
  <meta content="width=device-width,initial-scale=1.0" name="viewport"/>
  <link href="../../assets/css/article.css" rel="stylesheet"/>
  <link href="https://cdn.bootcss.com/highlight.js/9.15.8/styles/github.min.css" rel="stylesheet"/>
  <noscript>
   <link href="../../assets/css/noscript.css" rel="stylesheet"/>
  </noscript>
 </head>
 <body>
  <div id="app">
  </div>
  <!-- built files will be auto injected -->
 </body>
 <body class="is-preload">
  <!-- Wrapper -->
  <div id="wrapper">
   <!-- Main -->
   <div id="main">
    <article id="article">
     <h1 id="soul">
      Soul网关源码解析（七）限流插件初探
     </h1>
     <hr/>
     <h2 id="_1">
      简介
     </h2>
     <p>
      前面的文章中对处理流程探索的差不多了，今天来探索下限流插件:resilience4j
     </p>
     <h2 id="_2">
      示例运行
     </h2>
     <h3 id="_3">
      环境配置
     </h3>
     <p>
      启动下MySQL和redis
     </p>
     <p>
      ```shell script
docker run -dit --name redis -p 6379:6379 redis
docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:latest
     </p>
     <div class="codehilite">
      <pre><span></span><code>### Soul-Admin启动及相关配置
<span class="ni">&amp;ensp;&amp;ensp;&amp;ensp;&amp;ensp;</span>运行Soul-admin，进入管理界面：系统管理 --&gt; 插件管理 --&gt; resilience4j ，点击编辑，把它开启

<span class="ni">&amp;ensp;&amp;ensp;&amp;ensp;&amp;ensp;</span>进入管理界面的插件列表：resilience4j 添加选择器和规则，这里安装divide插件的匹配方式配的，让divide的/http前缀的接口都走限流（因为使用测试时官方本身自带的HTTP测试）

<span class="ni">&amp;ensp;&amp;ensp;&amp;ensp;&amp;ensp;</span>规则配置中：token filling number 要设置大于0，不然会报错

<span class="ni">&amp;ensp;&amp;ensp;&amp;ensp;&amp;ensp;</span>circuit enable 要设置为0，判断的时候走限流的逻辑

<span class="ni">&amp;ensp;&amp;ensp;&amp;ensp;&amp;ensp;</span>其他的：fallback uri 随便填个路径，其他的参数都可填1

### Soul-Bootstrap配置启动
<span class="ni">&amp;ensp;&amp;ensp;&amp;ensp;&amp;ensp;</span>在Soul-Bootstrap中进入相关的依赖，大致如下：

```xml
<span class="c">&lt;!-- soul resilience4j plugin start--&gt;</span>
  <span class="nt">&lt;dependency&gt;</span>
      <span class="nt">&lt;groupId&gt;</span>org.dromara<span class="nt">&lt;/groupId&gt;</span>
      <span class="nt">&lt;artifactId&gt;</span>soul-spring-boot-starter-plugin-resilience4j<span class="nt">&lt;/artifactId&gt;</span>
       <span class="nt">&lt;version&gt;</span><span class="cp">${</span><span class="n">last</span><span class="o">.</span><span class="n">version</span><span class="cp">}</span><span class="nt">&lt;/version&gt;</span>
  <span class="nt">&lt;/dependency&gt;</span>
  <span class="c">&lt;!-- soul resilience4j plugin end--&gt;</span>
</code></pre>
     </div>
     <p>
      启动Soul-Bootstrap
     </p>
     <h3 id="http">
      HTTP示例启动
     </h3>
     <p>
      启动：soul-examples --&gt; soul-examples-http --&gt; SoulTestHttpApplication
     </p>
     <p>
      进入管理界面的：插件列表 --&gt; divide 能看到相关的注册接口信息
     </p>
     <p>
      访问： http://127.0.0.1:9195/http/order/findById?id=1111
     </p>
     <p>
      成功运行，下面开始源码debug
     </p>
     <div class="codehilite">
      <pre><span></span><code><span class="p">{</span>
    <span class="nt">"id"</span><span class="p">:</span> <span class="s2">"1111"</span><span class="p">,</span>
    <span class="nt">"name"</span><span class="p">:</span> <span class="s2">"hello world findById"</span>
<span class="p">}</span>
</code></pre>
     </div>
     <h2 id="debug">
      源码Debug
     </h2>
     <h3 id="_4">
      限流流程顺序跟踪确认
     </h3>
     <p>
      根据前面的文章，对处理流程基本上有个清晰的认识了，我们通过前面的调试，知道 RateLimiterPlugin 是继承 AbstractSoulPlugin ,那它就会走和路由匹配相关的逻辑，如下面对代码所示。匹配成功后才走 doExcute 限流逻辑
     </p>
     <div class="codehilite">
      <pre><span></span><code>    <span class="err">#</span> <span class="n">AbstractSoulPlugin</span>
    <span class="c1">// 首先进行路由匹配</span>
    <span class="kd">public</span> <span class="n">Mono</span><span class="o">&lt;</span><span class="n">Void</span><span class="o">&gt;</span> <span class="nf">execute</span><span class="p">(</span><span class="kd">final</span> <span class="n">ServerWebExchange</span> <span class="n">exchange</span><span class="p">,</span> <span class="kd">final</span> <span class="n">SoulPluginChain</span> <span class="n">chain</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">String</span> <span class="n">pluginName</span> <span class="o">=</span> <span class="n">named</span><span class="p">();</span>
        <span class="kd">final</span> <span class="n">PluginData</span> <span class="n">pluginData</span> <span class="o">=</span> <span class="n">BaseDataCache</span><span class="p">.</span><span class="na">getInstance</span><span class="p">().</span><span class="na">obtainPluginData</span><span class="p">(</span><span class="n">pluginName</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">pluginData</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="n">pluginData</span><span class="p">.</span><span class="na">getEnabled</span><span class="p">())</span> <span class="p">{</span>
            <span class="kd">final</span> <span class="n">Collection</span><span class="o">&lt;</span><span class="n">SelectorData</span><span class="o">&gt;</span> <span class="n">selectors</span> <span class="o">=</span> <span class="n">BaseDataCache</span><span class="p">.</span><span class="na">getInstance</span><span class="p">().</span><span class="na">obtainSelectorData</span><span class="p">(</span><span class="n">pluginName</span><span class="p">);</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">CollectionUtils</span><span class="p">.</span><span class="na">isEmpty</span><span class="p">(</span><span class="n">selectors</span><span class="p">))</span> <span class="p">{</span>
                <span class="k">return</span> <span class="n">CheckUtils</span><span class="p">.</span><span class="na">checkSelector</span><span class="p">(</span><span class="n">pluginName</span><span class="p">,</span> <span class="n">exchange</span><span class="p">,</span> <span class="n">chain</span><span class="p">);</span>
            <span class="p">}</span>
            <span class="kd">final</span> <span class="n">SelectorData</span> <span class="n">selectorData</span> <span class="o">=</span> <span class="n">matchSelector</span><span class="p">(</span><span class="n">exchange</span><span class="p">,</span> <span class="n">selectors</span><span class="p">);</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">Objects</span><span class="p">.</span><span class="na">isNull</span><span class="p">(</span><span class="n">selectorData</span><span class="p">))</span> <span class="p">{</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">PluginEnum</span><span class="p">.</span><span class="na">WAF</span><span class="p">.</span><span class="na">getName</span><span class="p">().</span><span class="na">equals</span><span class="p">(</span><span class="n">pluginName</span><span class="p">))</span> <span class="p">{</span>
                    <span class="k">return</span> <span class="n">doExecute</span><span class="p">(</span><span class="n">exchange</span><span class="p">,</span> <span class="n">chain</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="kc">null</span><span class="p">);</span>
                <span class="p">}</span>
                <span class="k">return</span> <span class="n">CheckUtils</span><span class="p">.</span><span class="na">checkSelector</span><span class="p">(</span><span class="n">pluginName</span><span class="p">,</span> <span class="n">exchange</span><span class="p">,</span> <span class="n">chain</span><span class="p">);</span>
            <span class="p">}</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">selectorData</span><span class="p">.</span><span class="na">getLoged</span><span class="p">())</span> <span class="p">{</span>
                <span class="n">log</span><span class="p">.</span><span class="na">info</span><span class="p">(</span><span class="s">"{} selector success match , selector name :{}"</span><span class="p">,</span> <span class="n">pluginName</span><span class="p">,</span> <span class="n">selectorData</span><span class="p">.</span><span class="na">getName</span><span class="p">());</span>
            <span class="p">}</span>
            <span class="kd">final</span> <span class="n">List</span><span class="o">&lt;</span><span class="n">RuleData</span><span class="o">&gt;</span> <span class="n">rules</span> <span class="o">=</span> <span class="n">BaseDataCache</span><span class="p">.</span><span class="na">getInstance</span><span class="p">().</span><span class="na">obtainRuleData</span><span class="p">(</span><span class="n">selectorData</span><span class="p">.</span><span class="na">getId</span><span class="p">());</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">CollectionUtils</span><span class="p">.</span><span class="na">isEmpty</span><span class="p">(</span><span class="n">rules</span><span class="p">))</span> <span class="p">{</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">PluginEnum</span><span class="p">.</span><span class="na">WAF</span><span class="p">.</span><span class="na">getName</span><span class="p">().</span><span class="na">equals</span><span class="p">(</span><span class="n">pluginName</span><span class="p">))</span> <span class="p">{</span>
                    <span class="k">return</span> <span class="n">doExecute</span><span class="p">(</span><span class="n">exchange</span><span class="p">,</span> <span class="n">chain</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="kc">null</span><span class="p">);</span>
                <span class="p">}</span>
                <span class="k">return</span> <span class="n">CheckUtils</span><span class="p">.</span><span class="na">checkRule</span><span class="p">(</span><span class="n">pluginName</span><span class="p">,</span> <span class="n">exchange</span><span class="p">,</span> <span class="n">chain</span><span class="p">);</span>
            <span class="p">}</span>
            <span class="n">RuleData</span> <span class="n">rule</span><span class="p">;</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">selectorData</span><span class="p">.</span><span class="na">getType</span><span class="p">()</span> <span class="o">==</span> <span class="n">SelectorTypeEnum</span><span class="p">.</span><span class="na">FULL_FLOW</span><span class="p">.</span><span class="na">getCode</span><span class="p">())</span> <span class="p">{</span>
                <span class="c1">//get last</span>
                <span class="n">rule</span> <span class="o">=</span> <span class="n">rules</span><span class="p">.</span><span class="na">get</span><span class="p">(</span><span class="n">rules</span><span class="p">.</span><span class="na">size</span><span class="p">()</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                <span class="n">rule</span> <span class="o">=</span> <span class="n">matchRule</span><span class="p">(</span><span class="n">exchange</span><span class="p">,</span> <span class="n">rules</span><span class="p">);</span>
            <span class="p">}</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">Objects</span><span class="p">.</span><span class="na">isNull</span><span class="p">(</span><span class="n">rule</span><span class="p">))</span> <span class="p">{</span>
                <span class="k">return</span> <span class="n">CheckUtils</span><span class="p">.</span><span class="na">checkRule</span><span class="p">(</span><span class="n">pluginName</span><span class="p">,</span> <span class="n">exchange</span><span class="p">,</span> <span class="n">chain</span><span class="p">);</span>
            <span class="p">}</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">rule</span><span class="p">.</span><span class="na">getLoged</span><span class="p">())</span> <span class="p">{</span>
                <span class="n">log</span><span class="p">.</span><span class="na">info</span><span class="p">(</span><span class="s">"{} rule success match ,rule name :{}"</span><span class="p">,</span> <span class="n">pluginName</span><span class="p">,</span> <span class="n">rule</span><span class="p">.</span><span class="na">getName</span><span class="p">());</span>
            <span class="p">}</span>
            <span class="k">return</span> <span class="n">doExecute</span><span class="p">(</span><span class="n">exchange</span><span class="p">,</span> <span class="n">chain</span><span class="p">,</span> <span class="n">selectorData</span><span class="p">,</span> <span class="n">rule</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="k">return</span> <span class="n">chain</span><span class="p">.</span><span class="na">execute</span><span class="p">(</span><span class="n">exchange</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="err">#</span> <span class="n">RateLimiterPlugin</span>
    <span class="c1">// 匹配完成后走限流的逻辑</span>
    <span class="kd">protected</span> <span class="n">Mono</span><span class="o">&lt;</span><span class="n">Void</span><span class="o">&gt;</span> <span class="nf">doExecute</span><span class="p">(</span><span class="kd">final</span> <span class="n">ServerWebExchange</span> <span class="n">exchange</span><span class="p">,</span> <span class="kd">final</span> <span class="n">SoulPluginChain</span> <span class="n">chain</span><span class="p">,</span> <span class="kd">final</span> <span class="n">SelectorData</span> <span class="n">selector</span><span class="p">,</span> <span class="kd">final</span> <span class="n">RuleData</span> <span class="n">rule</span><span class="p">)</span> <span class="p">{</span>
        <span class="kd">final</span> <span class="n">SoulContext</span> <span class="n">soulContext</span> <span class="o">=</span> <span class="n">exchange</span><span class="p">.</span><span class="na">getAttribute</span><span class="p">(</span><span class="n">Constants</span><span class="p">.</span><span class="na">CONTEXT</span><span class="p">);</span>
        <span class="k">assert</span> <span class="n">soulContext</span> <span class="o">!=</span> <span class="kc">null</span><span class="p">;</span>
        <span class="c1">// 这里更加字符串转成对象，所有规则哪里不能乱填</span>
        <span class="n">Resilience4JHandle</span> <span class="n">resilience4JHandle</span> <span class="o">=</span> <span class="n">GsonUtils</span><span class="p">.</span><span class="na">getGson</span><span class="p">().</span><span class="na">fromJson</span><span class="p">(</span><span class="n">rule</span><span class="p">.</span><span class="na">getHandle</span><span class="p">(),</span> <span class="n">Resilience4JHandle</span><span class="p">.</span><span class="na">class</span><span class="p">);</span>
        <span class="c1">// 这里判断 Circle enable 是否为1 走 combined的逻辑，但我们这次想走 limit 的逻辑，所以要填0</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">resilience4JHandle</span><span class="p">.</span><span class="na">getCircuitEnable</span><span class="p">()</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">return</span> <span class="n">combined</span><span class="p">(</span><span class="n">exchange</span><span class="p">,</span> <span class="n">chain</span><span class="p">,</span> <span class="n">rule</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="k">return</span> <span class="n">rateLimiter</span><span class="p">(</span><span class="n">exchange</span><span class="p">,</span> <span class="n">chain</span><span class="p">,</span> <span class="n">rule</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="c1">// 到这有些复杂，看的不是太懂，只能继续跟下去</span>
    <span class="kd">private</span> <span class="n">Mono</span><span class="o">&lt;</span><span class="n">Void</span><span class="o">&gt;</span> <span class="nf">rateLimiter</span><span class="p">(</span><span class="kd">final</span> <span class="n">ServerWebExchange</span> <span class="n">exchange</span><span class="p">,</span> <span class="kd">final</span> <span class="n">SoulPluginChain</span> <span class="n">chain</span><span class="p">,</span> <span class="kd">final</span> <span class="n">RuleData</span> <span class="n">rule</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">ratelimiterExecutor</span><span class="p">.</span><span class="na">run</span><span class="p">(</span>
                <span class="n">chain</span><span class="p">.</span><span class="na">execute</span><span class="p">(</span><span class="n">exchange</span><span class="p">),</span> <span class="n">fallback</span><span class="p">(</span><span class="n">ratelimiterExecutor</span><span class="p">,</span> <span class="n">exchange</span><span class="p">,</span> <span class="kc">null</span><span class="p">),</span> <span class="n">Resilience4JBuilder</span><span class="p">.</span><span class="na">build</span><span class="p">(</span><span class="n">rule</span><span class="p">))</span>
                <span class="p">.</span><span class="na">onErrorResume</span><span class="p">(</span><span class="n">throwable</span> <span class="o">-&gt;</span> <span class="n">ratelimiterExecutor</span><span class="p">.</span><span class="na">withoutFallback</span><span class="p">(</span><span class="n">exchange</span><span class="p">,</span> <span class="n">throwable</span><span class="p">));</span>
    <span class="p">}</span>
</code></pre>
     </div>
     <p>
      plugin前面代码还是看的懂，但rateLimiter开始就有些迷糊，流式编程的知识用上都看不懂了，但大致知道是进行限流逻辑
     </p>
     <div class="codehilite">
      <pre><span></span><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">RateLimiterExecutor</span> <span class="kd">implements</span> <span class="n">Executor</span> <span class="p">{</span>

    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="n">Mono</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="nf">run</span><span class="p">(</span><span class="kd">final</span> <span class="n">Mono</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="n">toRun</span><span class="p">,</span> <span class="kd">final</span> <span class="n">Function</span><span class="o">&lt;</span><span class="n">Throwable</span><span class="p">,</span> <span class="n">Mono</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;&gt;</span> <span class="n">fallback</span><span class="p">,</span> <span class="kd">final</span> <span class="n">Resilience4JConf</span> <span class="n">conf</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">// 生成限流器</span>
        <span class="n">RateLimiter</span> <span class="n">rateLimiter</span> <span class="o">=</span> <span class="n">Resilience4JRegistryFactory</span><span class="p">.</span><span class="na">rateLimiter</span><span class="p">(</span><span class="n">conf</span><span class="p">.</span><span class="na">getId</span><span class="p">(),</span> <span class="n">conf</span><span class="p">.</span><span class="na">getRateLimiterConfig</span><span class="p">());</span>
        <span class="c1">// 应该是在这触发的限流逻辑</span>
        <span class="n">Mono</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="n">to</span> <span class="o">=</span> <span class="n">toRun</span><span class="p">.</span><span class="na">transformDeferred</span><span class="p">(</span><span class="n">RateLimiterOperator</span><span class="p">.</span><span class="na">of</span><span class="p">(</span><span class="n">rateLimiter</span><span class="p">));</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">fallback</span> <span class="o">!=</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">return</span> <span class="n">to</span><span class="p">.</span><span class="na">onErrorResume</span><span class="p">(</span><span class="n">fallback</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="k">return</span> <span class="n">to</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre>
     </div>
     <p>
      继续跟到上面那个类，我们看到了明显的生成限流器的逻辑，但有个让疑惑的是，因为返回的Mono，但没有看到明显的限流触发逻辑。在没有响应式编程的基础的时候感觉很懵，目前也没去定位真正的触发代码是在哪？但猜测是在上面注释中标注的那段触发的
     </p>
     <p>
      因为响应式，没有办法跟下去了，我们只能另找路径，看看具体的限流逻辑是什么样的
     </p>
     <p>
      通过上面知道：RateLimiter 是限流器，我们查看它的具体实现
     </p>
     <p>
      发现是一个接口，我们看看它有哪些实现，发现有两个： SemaphoreBasedRateLimiter 和 AtomicRateLimiter
     </p>
     <p>
      因为不知道用的哪个，我们在这两个类中可能会执行的函数都给打上断点
     </p>
     <p>
      重启发送请求，不断的跳断点，终于进入了一个限流器的类： AtomicRateLimiter ，大致如下
     </p>
     <div class="codehilite">
      <pre><span></span><code>    <span class="err">#</span> <span class="n">AtomicRateLimiter</span>
    <span class="kd">public</span> <span class="kt">long</span> <span class="nf">reservePermission</span><span class="p">(</span><span class="kd">final</span> <span class="kt">int</span> <span class="n">permits</span><span class="p">)</span> <span class="p">{</span>
        <span class="kt">long</span> <span class="n">timeoutInNanos</span> <span class="o">=</span> <span class="p">((</span><span class="n">AtomicRateLimiter</span><span class="p">.</span><span class="na">State</span><span class="p">)</span><span class="k">this</span><span class="p">.</span><span class="na">state</span><span class="p">.</span><span class="na">get</span><span class="p">()).</span><span class="na">config</span><span class="p">.</span><span class="na">getTimeoutDuration</span><span class="p">().</span><span class="na">toNanos</span><span class="p">();</span>
        <span class="n">AtomicRateLimiter</span><span class="p">.</span><span class="na">State</span> <span class="n">modifiedState</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="na">updateStateWithBackOff</span><span class="p">(</span><span class="n">permits</span><span class="p">,</span> <span class="n">timeoutInNanos</span><span class="p">);</span>
        <span class="kt">boolean</span> <span class="n">canAcquireImmediately</span> <span class="o">=</span> <span class="n">modifiedState</span><span class="p">.</span><span class="na">nanosToWait</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="n">L</span><span class="p">;</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">canAcquireImmediately</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">this</span><span class="p">.</span><span class="na">publishRateLimiterEvent</span><span class="p">(</span><span class="kc">true</span><span class="p">,</span> <span class="n">permits</span><span class="p">);</span>
            <span class="k">return</span> <span class="mi">0</span><span class="n">L</span><span class="p">;</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="kt">boolean</span> <span class="n">canAcquireInTime</span> <span class="o">=</span> <span class="n">timeoutInNanos</span> <span class="o">&gt;=</span> <span class="n">modifiedState</span><span class="p">.</span><span class="na">nanosToWait</span><span class="p">;</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">canAcquireInTime</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">this</span><span class="p">.</span><span class="na">publishRateLimiterEvent</span><span class="p">(</span><span class="kc">true</span><span class="p">,</span> <span class="n">permits</span><span class="p">);</span>
                <span class="k">return</span> <span class="n">modifiedState</span><span class="p">.</span><span class="na">nanosToWait</span><span class="p">;</span>
            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                <span class="k">this</span><span class="p">.</span><span class="na">publishRateLimiterEvent</span><span class="p">(</span><span class="kc">false</span><span class="p">,</span> <span class="n">permits</span><span class="p">);</span>
                <span class="k">return</span> <span class="o">-</span><span class="mi">1L</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
</code></pre>
     </div>
     <p>
      具体实现逻辑，不是我们此次关注的目的，此次是想看它在plugin中处理的流程顺序如何
     </p>
     <p>
      和前面几篇一样，我们在： SoulWebHandler 打上断点，看看限流器的执行顺序是什么样的
     </p>
     <p>
      通过debug，我们发现顺序和我们预期的基本一致：在进入 RateLimiterPlugin 插件执行的时候，执行的断点也到了限流器（AtomicRateLimiter),等限流器逻辑执行完毕，divide等插件才开始执行
     </p>
     <h3 id="mono">
      关于执行处罚和Mono的一些思考
     </h3>
     <p>
      我们看一下下面限流执行的代码：
     </p>
     <div class="codehilite">
      <pre><span></span><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">RateLimiterExecutor</span> <span class="kd">implements</span> <span class="n">Executor</span> <span class="p">{</span>

    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="n">Mono</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="nf">run</span><span class="p">(</span><span class="kd">final</span> <span class="n">Mono</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="n">toRun</span><span class="p">,</span> <span class="kd">final</span> <span class="n">Function</span><span class="o">&lt;</span><span class="n">Throwable</span><span class="p">,</span> <span class="n">Mono</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;&gt;</span> <span class="n">fallback</span><span class="p">,</span> <span class="kd">final</span> <span class="n">Resilience4JConf</span> <span class="n">conf</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">// 生成限流器</span>
        <span class="n">RateLimiter</span> <span class="n">rateLimiter</span> <span class="o">=</span> <span class="n">Resilience4JRegistryFactory</span><span class="p">.</span><span class="na">rateLimiter</span><span class="p">(</span><span class="n">conf</span><span class="p">.</span><span class="na">getId</span><span class="p">(),</span> <span class="n">conf</span><span class="p">.</span><span class="na">getRateLimiterConfig</span><span class="p">());</span>
        <span class="c1">// 应该是在这触发的限流逻辑</span>
        <span class="n">Mono</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="n">to</span> <span class="o">=</span> <span class="n">toRun</span><span class="p">.</span><span class="na">transformDeferred</span><span class="p">(</span><span class="n">RateLimiterOperator</span><span class="p">.</span><span class="na">of</span><span class="p">(</span><span class="n">rateLimiter</span><span class="p">));</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">fallback</span> <span class="o">!=</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">return</span> <span class="n">to</span><span class="p">.</span><span class="na">onErrorResume</span><span class="p">(</span><span class="n">fallback</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="k">return</span> <span class="n">to</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre>
     </div>
     <p>
      返回的一个Mono
     </p>
     <p>
      我们再看看divide之类的,也是返回的Mono
     </p>
     <div class="codehilite">
      <pre><span></span><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">DividePlugin</span> <span class="kd">extends</span> <span class="n">AbstractSoulPlugin</span> <span class="p">{</span>

    <span class="nd">@Override</span>
    <span class="kd">protected</span> <span class="n">Mono</span><span class="o">&lt;</span><span class="n">Void</span><span class="o">&gt;</span> <span class="nf">doExecute</span><span class="p">(</span><span class="kd">final</span> <span class="n">ServerWebExchange</span> <span class="n">exchange</span><span class="p">,</span> <span class="kd">final</span> <span class="n">SoulPluginChain</span> <span class="n">chain</span><span class="p">,</span> <span class="kd">final</span> <span class="n">SelectorData</span> <span class="n">selector</span><span class="p">,</span> <span class="kd">final</span> <span class="n">RuleData</span> <span class="n">rule</span><span class="p">)</span> <span class="p">{</span>
        <span class="kd">final</span> <span class="n">SoulContext</span> <span class="n">soulContext</span> <span class="o">=</span> <span class="n">exchange</span><span class="p">.</span><span class="na">getAttribute</span><span class="p">(</span><span class="n">Constants</span><span class="p">.</span><span class="na">CONTEXT</span><span class="p">);</span>
        <span class="k">assert</span> <span class="n">soulContext</span> <span class="o">!=</span> <span class="kc">null</span><span class="p">;</span>
        <span class="kd">final</span> <span class="n">DivideRuleHandle</span> <span class="n">ruleHandle</span> <span class="o">=</span> <span class="n">GsonUtils</span><span class="p">.</span><span class="na">getInstance</span><span class="p">().</span><span class="na">fromJson</span><span class="p">(</span><span class="n">rule</span><span class="p">.</span><span class="na">getHandle</span><span class="p">(),</span> <span class="n">DivideRuleHandle</span><span class="p">.</span><span class="na">class</span><span class="p">);</span>
        <span class="kd">final</span> <span class="n">List</span><span class="o">&lt;</span><span class="n">DivideUpstream</span><span class="o">&gt;</span> <span class="n">upstreamList</span> <span class="o">=</span> <span class="n">UpstreamCacheManager</span><span class="p">.</span><span class="na">getInstance</span><span class="p">().</span><span class="na">findUpstreamListBySelectorId</span><span class="p">(</span><span class="n">selector</span><span class="p">.</span><span class="na">getId</span><span class="p">());</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">CollectionUtils</span><span class="p">.</span><span class="na">isEmpty</span><span class="p">(</span><span class="n">upstreamList</span><span class="p">))</span> <span class="p">{</span>
            <span class="n">log</span><span class="p">.</span><span class="na">error</span><span class="p">(</span><span class="s">"divide upstream configuration error： {}"</span><span class="p">,</span> <span class="n">rule</span><span class="p">.</span><span class="na">toString</span><span class="p">());</span>
            <span class="n">Object</span> <span class="n">error</span> <span class="o">=</span> <span class="n">SoulResultWrap</span><span class="p">.</span><span class="na">error</span><span class="p">(</span><span class="n">SoulResultEnum</span><span class="p">.</span><span class="na">CANNOT_FIND_URL</span><span class="p">.</span><span class="na">getCode</span><span class="p">(),</span> <span class="n">SoulResultEnum</span><span class="p">.</span><span class="na">CANNOT_FIND_URL</span><span class="p">.</span><span class="na">getMsg</span><span class="p">(),</span> <span class="kc">null</span><span class="p">);</span>
            <span class="k">return</span> <span class="n">WebFluxResultUtils</span><span class="p">.</span><span class="na">result</span><span class="p">(</span><span class="n">exchange</span><span class="p">,</span> <span class="n">error</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="kd">final</span> <span class="n">String</span> <span class="n">ip</span> <span class="o">=</span> <span class="n">Objects</span><span class="p">.</span><span class="na">requireNonNull</span><span class="p">(</span><span class="n">exchange</span><span class="p">.</span><span class="na">getRequest</span><span class="p">().</span><span class="na">getRemoteAddress</span><span class="p">()).</span><span class="na">getAddress</span><span class="p">().</span><span class="na">getHostAddress</span><span class="p">();</span>
        <span class="n">DivideUpstream</span> <span class="n">divideUpstream</span> <span class="o">=</span> <span class="n">LoadBalanceUtils</span><span class="p">.</span><span class="na">selector</span><span class="p">(</span><span class="n">upstreamList</span><span class="p">,</span> <span class="n">ruleHandle</span><span class="p">.</span><span class="na">getLoadBalance</span><span class="p">(),</span> <span class="n">ip</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">Objects</span><span class="p">.</span><span class="na">isNull</span><span class="p">(</span><span class="n">divideUpstream</span><span class="p">))</span> <span class="p">{</span>
            <span class="n">log</span><span class="p">.</span><span class="na">error</span><span class="p">(</span><span class="s">"divide has no upstream"</span><span class="p">);</span>
            <span class="n">Object</span> <span class="n">error</span> <span class="o">=</span> <span class="n">SoulResultWrap</span><span class="p">.</span><span class="na">error</span><span class="p">(</span><span class="n">SoulResultEnum</span><span class="p">.</span><span class="na">CANNOT_FIND_URL</span><span class="p">.</span><span class="na">getCode</span><span class="p">(),</span> <span class="n">SoulResultEnum</span><span class="p">.</span><span class="na">CANNOT_FIND_URL</span><span class="p">.</span><span class="na">getMsg</span><span class="p">(),</span> <span class="kc">null</span><span class="p">);</span>
            <span class="k">return</span> <span class="n">WebFluxResultUtils</span><span class="p">.</span><span class="na">result</span><span class="p">(</span><span class="n">exchange</span><span class="p">,</span> <span class="n">error</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="c1">// set the http url</span>
        <span class="n">String</span> <span class="n">domain</span> <span class="o">=</span> <span class="n">buildDomain</span><span class="p">(</span><span class="n">divideUpstream</span><span class="p">);</span>
        <span class="n">String</span> <span class="n">realURL</span> <span class="o">=</span> <span class="n">buildRealURL</span><span class="p">(</span><span class="n">domain</span><span class="p">,</span> <span class="n">soulContext</span><span class="p">,</span> <span class="n">exchange</span><span class="p">);</span>
        <span class="n">exchange</span><span class="p">.</span><span class="na">getAttributes</span><span class="p">().</span><span class="na">put</span><span class="p">(</span><span class="n">Constants</span><span class="p">.</span><span class="na">HTTP_URL</span><span class="p">,</span> <span class="n">realURL</span><span class="p">);</span>
        <span class="c1">// set the http timeout</span>
        <span class="n">exchange</span><span class="p">.</span><span class="na">getAttributes</span><span class="p">().</span><span class="na">put</span><span class="p">(</span><span class="n">Constants</span><span class="p">.</span><span class="na">HTTP_TIME_OUT</span><span class="p">,</span> <span class="n">ruleHandle</span><span class="p">.</span><span class="na">getTimeout</span><span class="p">());</span>
        <span class="n">exchange</span><span class="p">.</span><span class="na">getAttributes</span><span class="p">().</span><span class="na">put</span><span class="p">(</span><span class="n">Constants</span><span class="p">.</span><span class="na">HTTP_RETRY</span><span class="p">,</span> <span class="n">ruleHandle</span><span class="p">.</span><span class="na">getRetry</span><span class="p">());</span>
        <span class="k">return</span> <span class="n">chain</span><span class="p">.</span><span class="na">execute</span><span class="p">(</span><span class="n">exchange</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre>
     </div>
     <p>
      再看看我们非常熟悉： SoulWebHandler
     </p>
     <div class="codehilite">
      <pre><span></span><code>        <span class="kd">public</span> <span class="n">Mono</span><span class="o">&lt;</span><span class="n">Void</span><span class="o">&gt;</span> <span class="nf">execute</span><span class="p">(</span><span class="kd">final</span> <span class="n">ServerWebExchange</span> <span class="n">exchange</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">return</span> <span class="n">Mono</span><span class="p">.</span><span class="na">defer</span><span class="p">(()</span> <span class="o">-&gt;</span> <span class="p">{</span>
                <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="na">index</span> <span class="o">&lt;</span> <span class="n">plugins</span><span class="p">.</span><span class="na">size</span><span class="p">())</span> <span class="p">{</span>
                    <span class="n">SoulPlugin</span> <span class="n">plugin</span> <span class="o">=</span> <span class="n">plugins</span><span class="p">.</span><span class="na">get</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="na">index</span><span class="o">++</span><span class="p">);</span>
                    <span class="n">Boolean</span> <span class="n">skip</span> <span class="o">=</span> <span class="n">plugin</span><span class="p">.</span><span class="na">skip</span><span class="p">(</span><span class="n">exchange</span><span class="p">);</span>
                    <span class="k">if</span> <span class="p">(</span><span class="n">skip</span><span class="p">)</span> <span class="p">{</span>
                        <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="na">execute</span><span class="p">(</span><span class="n">exchange</span><span class="p">);</span>
                    <span class="p">}</span>
                    <span class="k">return</span> <span class="n">plugin</span><span class="p">.</span><span class="na">execute</span><span class="p">(</span><span class="n">exchange</span><span class="p">,</span> <span class="k">this</span><span class="p">);</span>
                <span class="p">}</span>
                <span class="k">return</span> <span class="n">Mono</span><span class="p">.</span><span class="na">empty</span><span class="p">();</span>
            <span class="p">});</span>
        <span class="p">}</span>
</code></pre>
     </div>
     <p>
      在上面函数中，通过英文，可以看到所有的Plugin都是返回一个Mono
     </p>
     <p>
      我们结合响应式编程的相关概念：发布订阅。也就是说，这些plugin Mono 会发布到一个队列中，当订阅的时候，就会取出来顺序执行
     </p>
     <p>
      订阅的逻辑大致在那呢，我们翻一翻我们第三篇分析:
      <a href="https://juejin.cn/post/6917866538712334343">
       Soul 网关源码阅读（三）请求处理概览
      </a>
     </p>
     <p>
      在类：HttpServerHandle ，找到很可疑的一段，猜测应该是这：
     </p>
     <div class="codehilite">
      <pre><span></span><code>    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">onStateChange</span><span class="p">(</span><span class="n">Connection</span> <span class="n">connection</span><span class="p">,</span> <span class="n">State</span> <span class="n">newState</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">newState</span> <span class="o">==</span> <span class="n">HttpServerState</span><span class="p">.</span><span class="na">REQUEST_RECEIVED</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">try</span> <span class="p">{</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">log</span><span class="p">.</span><span class="na">isDebugEnabled</span><span class="p">())</span> <span class="p">{</span>
                    <span class="n">log</span><span class="p">.</span><span class="na">debug</span><span class="p">(</span><span class="n">ReactorNetty</span><span class="p">.</span><span class="na">format</span><span class="p">(</span><span class="n">connection</span><span class="p">.</span><span class="na">channel</span><span class="p">(),</span> <span class="s">"Handler is being applied: {}"</span><span class="p">),</span> <span class="k">new</span> <span class="n">Object</span><span class="o">[]</span><span class="p">{</span><span class="k">this</span><span class="p">.</span><span class="na">handler</span><span class="p">});</span>
                <span class="p">}</span>

                <span class="n">HttpServerOperations</span> <span class="n">ops</span> <span class="o">=</span> <span class="p">(</span><span class="n">HttpServerOperations</span><span class="p">)</span><span class="n">connection</span><span class="p">;</span>
                <span class="c1">// 在这进行了发布和订阅，而handler.apply(ops, ops)会不断调用后面哪些plugin的逻辑</span>
                <span class="n">Mono</span><span class="p">.</span><span class="na">fromDirect</span><span class="p">((</span><span class="n">Publisher</span><span class="p">)</span><span class="k">this</span><span class="p">.</span><span class="na">handler</span><span class="p">.</span><span class="na">apply</span><span class="p">(</span><span class="n">ops</span><span class="p">,</span> <span class="n">ops</span><span class="p">)).</span><span class="na">subscribe</span><span class="p">(</span><span class="n">ops</span><span class="p">.</span><span class="na">disposeSubscriber</span><span class="p">());</span>
            <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="n">Throwable</span> <span class="n">var4</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">log</span><span class="p">.</span><span class="na">error</span><span class="p">(</span><span class="n">ReactorNetty</span><span class="p">.</span><span class="na">format</span><span class="p">(</span><span class="n">connection</span><span class="p">.</span><span class="na">channel</span><span class="p">(),</span> <span class="s">""</span><span class="p">),</span> <span class="n">var4</span><span class="p">);</span>
                <span class="n">connection</span><span class="p">.</span><span class="na">channel</span><span class="p">().</span><span class="na">close</span><span class="p">();</span>
            <span class="p">}</span>
        <span class="p">}</span>

    <span class="p">}</span>
</code></pre>
     </div>
     <p>
      而限流的Mono是在divide之前，所以限流就先执行了，大致示意图如下：
     </p>
     <p>
      <img alt="" src="./picture/pluginProcess.png"/>
     </p>
     <p>
      大意是：fromDirect 函数触发将 Plugin Mono 放到队列中；subscribe函数，触发执行，执行顺序先进先出，则GlobalPlugin先进去的，则先开始执行（图中Global先进的，把上方看做队列底部，理解意思就行）。那顺序就对应上了我们的调试猜想
     </p>
     <p>
      还没深入研究响应式编程，所以也有可能是错的
     </p>
     <h3 id="_5">
      疑问点
     </h3>
     <p>
      在下面这段生成限流器的逻辑中，好像每次请求过来都是进行一个新的生成，有没有可能进行复用，配置里面加一个字段，表示是否更新过，没有更新，我们就复用我们之前的限流器；有更新我们就新生成一个
     </p>
     <p>
      当然上面优化，需要在具体了解动态配置更新后，再看看是否可行
     </p>
     <p>
      也有可能是不熟悉Resilience4J,可能下面的代码中Resilience4JRegistryFactory本身实现了缓存复用
     </p>
     <div class="codehilite">
      <pre><span></span><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">RateLimiterExecutor</span> <span class="kd">implements</span> <span class="n">Executor</span> <span class="p">{</span>

    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="n">Mono</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="nf">run</span><span class="p">(</span><span class="kd">final</span> <span class="n">Mono</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="n">toRun</span><span class="p">,</span> <span class="kd">final</span> <span class="n">Function</span><span class="o">&lt;</span><span class="n">Throwable</span><span class="p">,</span> <span class="n">Mono</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;&gt;</span> <span class="n">fallback</span><span class="p">,</span> <span class="kd">final</span> <span class="n">Resilience4JConf</span> <span class="n">conf</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">// 生成限流器</span>
        <span class="n">RateLimiter</span> <span class="n">rateLimiter</span> <span class="o">=</span> <span class="n">Resilience4JRegistryFactory</span><span class="p">.</span><span class="na">rateLimiter</span><span class="p">(</span><span class="n">conf</span><span class="p">.</span><span class="na">getId</span><span class="p">(),</span> <span class="n">conf</span><span class="p">.</span><span class="na">getRateLimiterConfig</span><span class="p">());</span>
        <span class="c1">// 应该是在这触发的限流逻辑</span>
        <span class="n">Mono</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span> <span class="n">to</span> <span class="o">=</span> <span class="n">toRun</span><span class="p">.</span><span class="na">transformDeferred</span><span class="p">(</span><span class="n">RateLimiterOperator</span><span class="p">.</span><span class="na">of</span><span class="p">(</span><span class="n">rateLimiter</span><span class="p">));</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">fallback</span> <span class="o">!=</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">return</span> <span class="n">to</span><span class="p">.</span><span class="na">onErrorResume</span><span class="p">(</span><span class="n">fallback</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="k">return</span> <span class="n">to</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre>
     </div>
     <h2 id="_6">
      总结
     </h2>
     <p>
      本次文章大致探索了限流插件：resilience4j的使用配置。调试验证它的限流逻辑执行在plugin链中执行顺序，发现基本符合我们的猜想，限流逻辑的执行和plugin顺序一致
     </p>
     <p>
      还初步讨论提出了plugin链在Mono队列中的执行猜想，后面研究响应式编程的时候验证一下猜想是否正常
     </p>
     <p>
      最后提出了一些对限流器生成的一些优化疑问，看后面配置更新相关的分析的时候，是否能验证自己的猜想
     </p>
     <h2 id="_7">
      参考链接
     </h2>
     <ul>
      <li>
       <a href="https://dromara.org/zh-cn/docs/soul/plugin-resilience4j.html">
        resilience4j插件
       </a>
      </li>
      <li>
       <a href="https://blog.csdn.net/mickjoust/article/details/102411585">
        Resilience4j 源码解析（3）：限流模块 RateLimiter 与 常见限流算法
       </a>
      </li>
     </ul>
     <h2 id="soul_1">
      Soul网关源码分析文章列表
     </h2>
     <h3 id="github">
      Github
     </h3>
     <ul>
      <li>
       <a href="https://github.com/lw1243925457/SE-Notes/blob/master/profession/program/%E5%BC%80%E6%BA%90/soul/soul%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB1-%E6%A6%82%E8%A7%88.md">
        Soul 源码阅读（一） 概览
       </a>
      </li>
      <li>
       <a href="https://github.com/lw1243925457/SE-Notes/blob/master/profession/program/%E5%BC%80%E6%BA%90/soul/soul%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB2-%E5%88%9D%E6%AD%A5%E8%BF%90%E8%A1%8C.md">
        Soul 源码阅读（二）代码初步运行
       </a>
      </li>
      <li>
       <a href="https://github.com/lw1243925457/SE-Notes/blob/master/profession/program/%E5%BC%80%E6%BA%90/soul/soul%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB3-%E8%AF%B7%E6%B1%82%E5%A4%84%E7%90%86%E6%A6%82%E8%A7%88.md">
        Soul 源码阅读（三）HTTP请求处理概览
       </a>
      </li>
      <li>
       <a href="https://github.com/lw1243925457/SE-Notes/blob/master/profession/program/%E5%BC%80%E6%BA%90/soul/soul%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB4-dubbo%E8%AF%B7%E6%B1%82%E6%A6%82%E8%A7%88.md">
        Soul 网关源码阅读（四）Dubbo请求概览
       </a>
      </li>
      <li>
       <a href="https://github.com/lw1243925457/SE-Notes/blob/master/profession/program/%E5%BC%80%E6%BA%90/soul/soul%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB5-%E8%AF%B7%E6%B1%82%E7%B1%BB%E5%9E%8B%E6%8E%A2%E7%B4%A2.md">
        Soul网关源码阅读（五）请求类型探索
       </a>
      </li>
      <li>
       <p>
        <a href="https://github.com/lw1243925457/SE-Notes/blob/master/profession/program/%E5%BC%80%E6%BA%90/soul/soul%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB6-sofa%E8%AF%B7%E6%B1%82%E5%A4%84%E7%90%86%E6%A6%82%E8%A7%88.md">
         Soul 网关源码阅读（六）Sofa请求处理概览
        </a>
       </p>
      </li>
      <li>
       <p>
        <a href="https://github.com/lw1243925457/SE-Notes/blob/master/profession/program/%E5%BC%80%E6%BA%90/soul/soul%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB%E7%95%AA%E5%A4%96%E7%AF%871-HTTP%E7%A4%BA%E4%BE%8B%E5%8F%82%E6%95%B0%E8%AF%B7%E6%B1%82%E9%94%99%E8%AF%AF.md">
         Soul网关源码阅读番外篇（一） HTTP参数请求错误
        </a>
       </p>
      </li>
     </ul>
     <h3 id="_8">
      掘金
     </h3>
     <ul>
      <li>
       <a href="https://juejin.cn/post/6917864624423436296">
        Soul 网关源码阅读（一） 概览
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6917865804121767944">
        Soul 网关源码阅读（二）代码初步运行
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6917866538712334343">
        Soul 网关源码阅读（三）请求处理概览
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6917867369909977102">
        Soul 网关源码阅读（四）Dubbo请求概览
       </a>
      </li>
      <li>
       <a href="https://juejin.cn/post/6918575905962983438">
        Soul网关源码阅读（五）请求类型探索
       </a>
      </li>
      <li>
       <p>
        <a href="https://juejin.cn/post/6918736260467015693">
         Soul 网关源码阅读（六）Sofa请求处理概览
        </a>
       </p>
      </li>
      <li>
       <p>
        <a href="https://juejin.cn/post/6918947689564471309">
         Soul网关源码阅读番外篇（一） HTTP参数请求错误
        </a>
       </p>
      </li>
     </ul>
    </article>
   </div>
   <!-- Footer -->
   <footer id="footer">
    <p class="copyright">
     © Untitled. Design:
     <a href="https://html5up.net">
      HTML5 UP
     </a>
     .
    </p>
   </footer>
  </div>
  <!-- BG -->
  <div id="bg">
  </div>
  <!-- Scripts -->
  <script src="../assets/js/jquery.min.js">
  </script>
  <script src="../assets/js/browser.min.js">
  </script>
  <script src="../assets/js/breakpoints.min.js">
  </script>
  <script src="../assets/js/util.js">
  </script>
  <script src="../assets/js/main.js">
  </script>
 </body>
</html>
